package bigo.data;

import java.util.Map;
import org.apache.hadoop.io.GenericWritable;
import org.apache.hadoop.io.Writable;
import org.apache.hadoop.io.WritableName;

/**
 * The basic Vector gives the properties and methods that a vector subclass should have
 *
 * @author Song Liu (sl9885@bristol.ac.uk)
 */
public abstract class Vector implements Map<Integer, Float>, Writable {

    private int offset;

    /**
     * Create a Vector with a specific offset
     * @param offset
     */
    public Vector(int offset) {
        this.offset = offset;
    }

    /**
     * Wrapper
     */
    public static class Wrapper extends GenericWritable {

        private static Class[] CLASSES = {
            ArrayVector.class,
            HashVector.class,
            ArrayVectorCombo.class
        };

        protected Class[] getTypes() {
            return CLASSES;
        }

        public Wrapper() {
        }

        public Wrapper(Writable v) {
            set(v);
        }

        @Override
        public String toString() {
            return ((Vector) get()).toString();
        }
    }

    @Deprecated
    public abstract Vector duplicate();

    /**
     * generates a sub vector with given start and end position
     * @param from start position
     * @param to end position
     * @return
     */
    public abstract Vector subVector(int from, int to);

    public abstract int length();

    public void setOffset(int offset) {
        this.offset = offset;
    }

    public int getOffset() {
        return offset;
    }

    /**
     * May be optimized for performance 
     * @param m
     * @return
     */
    public ArrayVector multiplyWith(SmartMatrix m) {
        ArrayVector ret = new ArrayVector(
                m.getRowOffset(), m.getRowAt(m.getColumnOffset()).length());
        for (Integer i : keySet()) {
            for (Integer j : m.getRowAt(i).keySet()) {
                ret.getVector()[j-ret.getOffset()] += m.get(i, j) * get(i);
            }
        }
        return ret;
    }

    /**
     * May be optimized for performance
     * @param m
     * @return
     */
    public ArrayVector multiplyWithT(SmartMatrix m) {
        ArrayVector ret = new ArrayVector(m.getColumnOffset(), m.getNumRows());

        int startsFrom = m.getColumnOffset();
        for (int i = startsFrom; i < startsFrom + m.getNumRows(); i++) {
            float sum = 0;
            for (Integer j :
                    m.getRowAt(i).size() < size()
                    ? m.getRowAt(i).keySet() : keySet()) {
                if (get(j) != null) {
                    sum += get(j) * m.get(i, j);
                }
            }
            ret.put(i, sum);
        }
        return ret;
    }

    /**
     * Compute the result of multiplying one vector and one matrix
     * @param m
     * @return An ARRAYVECTOR stores the results
     */
    @Deprecated
    public ArrayVector multiplyWith(Matrix m) {
        ArrayVector ret = new ArrayVector(
                m.getRowVectorAt(m.getColumnOffset()).getOffset(),
                m.getRowVectorAt(m.getColumnOffset()).length());

        for (Integer i : keySet()) {
            for (Integer j : m.getRowVectorAt(i).keySet()) {
                if (ret.get(j) == null) {
                    ret.put(j, 0.0f);
                }
                ret.put(j, ret.get(j) + m.getRowVectorAt(i).get(j) * get(i));
            }
        }
        return ret;
    }

    /**
     * Computes the result of multiplying one vector and a transposed matrix
     * @param m
     * @return
     */
    @Deprecated
    public ArrayVector multiplyWithT(Matrix m) {
        ArrayVector ret = new ArrayVector(m.getColumnOffset(), m.getRowArrays().size());

        int startsFrom = m.getColumnOffset();
        for (int i = startsFrom; i < startsFrom + m.getRowArrays().size(); i++) {
            float sum = 0;
            for (Integer j : m.getRowVectorAt(i).keySet()) {
                if (get(j) != null) {
                    sum += get(j) * m.getRowVectorAt(i).get(j);
                }
            }
            ret.put(i, sum);
        }
        return ret;
    }

    @Override
    public String toString() {
        StringBuffer sb = new StringBuffer();
        for (Integer s : keySet()) {
            sb.append(s + "-" + get(s) + "\t");
        }
        return sb.toString();
    }
}
